package com.dcstudy.redis.controller.ioupgrade.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;

/**
 * @author lichenyang
 * @create 2021-06-24 10:07
 *
 * 演示使用 NIO 进行 不同客户端连接是否阻塞的问题
 *
 * NIO存在的问题:
 * 1. 如果 socket链接一多, ArrayList中的连接就会越来越多
 * 2. 在for循环中 一直遍历所有的 socket链接, 很有可能 80% socket连接中并没有数据, 但是还是将他们遍历了一遍
 * 3. 调用 read() 方法 需要用户态到内核态的转换 耗费性能和资源
 *
 * 解决方案: IO多路复用 直接将IO处理 交给内核态去处理
 */
public class RedisServerNIO {
    /**
     * 将所有的socket 链接存放到一个 ArrayList集合中
     */
    static ArrayList<SocketChannel> socketList = new ArrayList<>();
    static ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

    public static void main(String[] args) throws IOException {
        System.out.println("---------RedisServerNIO 启动等待中 ......");
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.bind(new InetSocketAddress("127.0.0.1", 6379));
        //设置为非阻塞模式
        serverSocket.configureBlocking(false);

        while (true) {

            for (SocketChannel element : socketList) {
                // 从socket中获取数据, 如果socket连接中存在数据, 则进行读取
                int read = element.read(byteBuffer);

                // 循环读取 socketList 中的数据, 如果
                // socketList 中存在数据, 则打印
                // socketList 没有数据, 查看 socketList 中下一个 socket链接是否存在数据
                if (read > 0) {
                    System.out.println("----- 读取数据 : " + read);
                    byteBuffer.flip();
                    byte[] bytes = new byte[read];
                    byteBuffer.get(bytes);
                    System.out.println(new String(bytes));
                    byteBuffer.clear();
                }
            }

            // 只要外部有 socket连接进来了, 就讲这个 socket 链接存放到 socketList中监听着
            SocketChannel socketChannel = serverSocket.accept();
            if (socketChannel != null) {
                System.out.println("----- 成功连接 : ");
                //设置为非阻塞模式
                socketChannel.configureBlocking(false);

                socketList.add(socketChannel);
                System.out.println("-----socketList size: " + socketList.size());
            }
        }
    }
}